# [七] Bean的实例化-循环依赖
循环依赖
如果存在A中有B属性,B中有A属性,那么当依赖注入的时候,就会产生当A还没创建完的时候,由于对B的创建再次返回创建A,最终造成循环依赖。
- Spring 中的循环依赖只会出现在单例实例无参构造函数实例化情况下。
- 原型模式下以及有参构造器注入的循环依赖Spring 无法解决,都会直接报错。
# 循环依赖的业务场景
以下代码就会产生循环依赖的问题,那么Spring 是如何解决的? 类:CircularRefA
@Service
public class CircularRefA {
CircularRefA(){
System.out.println("Instance of CircularRefA ======");
}
@Autowired
private CircularRefB circularRefB;
}
类:CircularRefB
@Service
public class CircularRefB {
CircularRefB(){
System.out.println("Instance of CircularRefB ======");
}
@Autowired
private CircularRefA circularRefA;
}
# 循环依赖的相关方法
单例模式下的循环依赖流程
├─
getBean ① 实例化入口│ ├─
doGetBean│ │ ├─
getSingleton ② 查看缓存里是否有实例│ │ ├─
getObjectForBeanInstance ③ 如果缓存有实例,直接返回实例│ │ ├─
getSingletond ④ 缓存没有实例,创建实例│ │ │ ├─
beforeSingletonCreation ⑤ 添加beanName到正在实例化的bean的set集合│ │ │ ├─
createBean│ │ │ │ ├─
doCreateBean ⑥ 执行创建实例│ │ │ │ │ ├─
createBeanInstance ⑦ 创建实例过程│ │ │ │ │ │ ├─
determineConstructorsFromBeanPostProcessors ⑧ 如果构造函数有@Autowired│ │ │ │ │ │ │ ├─
autowireConstructor ⑨ 执行构造器实例过程不会走到步骤 ⑪│ │ │ │ │ │ │ │ └─
getBean 返回 ① 实例化入口│ │ │ │ │ ├─
applyMergedBeanDefinitionPostProcessors ⑩ 注解的搜集和装配过程│ │ │ │ │ ├─
earlySingletonExposure ⑪ 暴露还没有完全实例化完成的 bean│ │ │ │ │ ├─
addSingletonFactory ⑫ 将提前暴露的 bean,放到三级缓存│ │ │ │ │ ├─
populateBean ⑬ 注依赖注入的核心过程│ │ │ │ │ │ ├─
inject│ │ │ │ │ │ │ ├─
AutowiredFieldElement.inject ⑭ 有@Autowired注解域的注入│ │ │ │ │ │ │ │ └─
getBean 返回①实例化入口│ │ │ │ │ │ │ ├─
AutowiredMethodElement.inject ⑭ 有@Autowired注解方法的注入│ │ │ │ │ │ │ │ └─
getBean 返回①实例化入口│ │ │ │ │ ├─
initializeBean ⑮ bean 实例化和IOC依赖注入完以后的增强处理过程│ │ │ │ │ └─
registerDisposableBeanIfNecessary ⑮ bean 的销毁过程│ │ │ ├─
afterSingletonCreation ⑯ 实例化完成,将beanName从正在实例化的bean的set集合中删除│ │ │ └─
addSingleton ⑰ 创建完成后,将完全实例化后的bean 放到一级缓存
# 循环依赖的步骤说明
CircularRefA 简称为 A,CircularRefB 简称为B,循环依赖步骤如下:
- 1、A类首先从①
实例化入口
进来,会一直执行到⑦创建实例后
,执行⑫设置三级缓存
; - 2、A类 继续执行⑬
populateBean
进行依赖注入,这里触发了 B类属性的 getBean操作,重新返回①实例化入口
; - 3、B类一直执行到⑦
创建实例
后,执行⑫设置三级缓存
; - 4、B类继续执行⑬
populateBean
进行依赖注入,这里触发了 A类属性的 getBean操作,重新返回①实例化入口
; - 5、A类之前正在实例化,⑫
singletonsCurrentlyInCreation
集合中有已经有这个 A类了,三级缓存里面也有了,所以这时候是从三级缓存中拿到的提前暴露的A实例,该实例还没有进行 B类属性的依赖注入的,B类属性为空。 - 6、B类拿到了 A提前暴露的实例后,顺利的执行完⑬
依赖注入
,执行⑰创建
完成后,将完全实例化后的bean⑰放到一级缓存
,至此B类的实例化已经完全做完; - 7、由于B类的实例化是由 A类实例化中 B属性的依赖注入触发的 getBean操作进行的,现在 B已经实例化,所以 A类中 B属性就可以完成⑬
populateBean
了,这时候 A类 B属性已经有值了; - 8、B类中的 A属性指向的就是 A类实例堆空间,所以这时候 B类中 A属性也会有值了;
- 9、此时A类顺利的得到了B属性的注入值,也完成了自己的初始化流程,也会将将全实例化后的bean ⑰
放到一级缓存
; - 10、循环依赖正式结束。
# 循环依赖的补充说明
如果是在
createBeanInstance
中,当前正在实例化的bean中有@Autowired注解的构造函数,触发getBean
org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
// We must check each value to see whether it requires a runtime reference
// to another bean to be resolved.
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
}
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
// 触发getBean 实例化
bean = this.beanFactory.getParentBeanFactory().getBean(refName);
}
如果是在
populateBean
中,@Autowired注解的构造函数依赖注入,并且注入参数是引用类型的话,最终也会触发 getBean进行实例化
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
//寻找当前正在实例化的bean中有@Autowired注解的构造函数
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
//如果ctors不为空,就说明构造函数上有@Autowired注解
return autowireConstructor(beanName, mbd, ctors, args);
}
org.springframework.beans.factory.support.DefaultListableBeanFactory
if (instanceCandidate instanceof Class) {
//在这里真正拿到了构造函数中依赖的实例
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
org.springframework.beans.factory.config.DependencyDescriptor
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
// 将引用类型实例化
return beanFactory.getBean(beanName);
}
# 循环依赖的流程图
# Bean的销毁
首先我们来看一下⑯ Bean的销毁过程
进入 doCreateBean () 方法
类文件: org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory
try {
...
//注册bean销毁时的类DisposableBeanAdapter
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
进入 registerDisposableBeanIfNecessary () 方法
类文件: org.springframework.beans.factory.support.
AbstractBeanFactory
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
// 如果是单例 bean
if (mbd.isSingleton()) {
// 对这个bean注册一个销毁的Adapter对象,对给定的bean执行销毁工作
// 过滤了 DestructionAwareBeanPostProcessor 类型的接口
registerDisposableBean(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
else {
// 拿到bean的作用域
Scope scope = this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
}
scope.registerDestructionCallback(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
}
}
在 bean创建完成后就会对这个 bean注册一个销毁的
Adapter对象
销毁的核心类
DisposableBeanAdapter
就是负责bean销毁
的类。在这个类中收集了该bean 是否实现了DisposableBean
接口,是否配置destroy-method
属性,如果有则 执行其destroy() 方法。
问题
Bean的销毁过程是什么时候触发的呢?
答案
- 在Spring 中执行显式关闭上下文,关闭时候会执行实现DisposableBean接口类中的destroy() 方法,如:
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring.xml"});
// 以下两种显式关闭都会调用实现了DisposableBean接口类中的destroy() 方法
// 1、显式关闭上下文
context.close();
// 2、显式关闭上下文
context.registerShutdownHook();
- 在Web环境中,当
Tomcat关闭
的时候就会调用到servlet中的销毁方法
,在这个方法中就会最终也会掉用到Spring中DisposableBeanAdapter
类的destroy()
方法,该方法就会根据前面的收集进行调用。
servlet中的销毁方法
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
// servlet中的销毁方法
public void contextDestroyed(ServletContextEvent event) {
// 该方法最终会调用Spring的DisposableBeanAdapter类的destroy()
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}